﻿using System;
using System.Collections.Generic;

namespace MTShader
{
    public class TextTool
    {
        public static StringNode FromCode(string code)
        {
            Antlr4.Runtime.AntlrInputStream input = new Antlr4.Runtime.AntlrInputStream(code);
            shader_parser.ushaderParser.ShaderContext root = null;
            string[] rulenames;
            string[] tokentypename;

            {
                //语法分析
                var lex = new shader_parser.ushaderLexer(input);//这是antlr帮我们生成的词法解释器
                var tokenstream = new Antlr4.Runtime.CommonTokenStream(lex);
                var parser = new shader_parser.ushaderParser(tokenstream);//这是antlr帮我们生成的语法解释器

                //parser.ErrorHandler+=可以在此时添加错误输出过程
                root = parser.shader();//得到语法中的 shader 节点,此时才开始匹配
                rulenames = parser.RuleNames;

                if (parser.NumberOfSyntaxErrors > 0 || parser.MatchedEndOfFile == false)
                {
                    throw new Exception("语法未完全匹配");//可以通过
                }
            }
            //因为anltr 没有提供机制供我们拿到注释 空格 缩进 这些排版信息，故而，我们需要自己给他管理起来
            //找一个/n为分界， /n之前的内容算前面的，/n之后的内容算后面的

            Dictionary<int, string> srclexs = new Dictionary<int, string>();//原始文本
            //用token index做key

            Dictionary<int, string> emptytext_fore = new Dictionary<int, string>();//外挂文本
            Dictionary<int, string> emptytext_back = new Dictionary<int, string>();//外挂文本 后置
            //用token index做key

            {//检索外挂文本
                input.Seek(0);
                var lexcheckeempty = new shader_parser.ushaderLexer(input);//这是antlr帮我们生成的词法解释器
                var ts = lexcheckeempty.GetAllTokens();

                tokentypename = new string[lexcheckeempty.TokenTypeMap.Count];
                foreach (var t in lexcheckeempty.TokenTypeMap)
                {
                    if (t.Value >= 0)
                        tokentypename[t.Value] = t.Key;
                }
                int lastindex = 0;
                for (var i = 0; i < ts.Count; i++)
                {
                    var t = ts[i];
                    int ibegin = t.StartIndex;
                    int iend = t.StopIndex;
                    //存储原始文本
                    var tokenstr = code.Substring(ibegin, iend + 1 - ibegin);
                    srclexs[i] = tokenstr;


                    if (ibegin > lastindex)
                    {//有空白
                        var skipstr = code.Substring(lastindex, ibegin - lastindex);
                        var splitindex = skipstr.IndexOf("\r\n");
                        if (splitindex > 0)
                        {
                            var skipstr2 = skipstr.Substring(splitindex + 2);
                            emptytext_fore[i] = skipstr2;//作为这个token的前置外挂

                            skipstr = skipstr.Substring(0, splitindex + 2);
                        }
                        emptytext_back[i - 1] = skipstr;//作为上一个token的后置外挂
                    }


                    lastindex = iend + 1;
                }
                var finalstr = code.Substring(lastindex);
                if (finalstr.Length > 0)
                    emptytext_back[ts.Count - 1] = finalstr;
            }




            //ANTLR 也帮我们实现了visit模式，但是ast都有了，我还何必用你的visit呢？
            //shader_parser.ushaderBaseVisitor<Result>
            return StringNode.Parse(root, srclexs, rulenames, tokentypename, emptytext_fore, emptytext_back);
        }
        public static string FormatShader(StringNode node, bool withtreeinfo = false)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            if (withtreeinfo)
            {
                node.FormatWithTreeInfo(sb);
            }
            else
            {
                node.Format(sb);
            }
            return sb.ToString();
        }
    }

    //我们组装一个简化的AST，主要做字符串修改的StringNode，这我们叫他AST
    public class StringNode
    {
        public string type;
        public string empty_fore;//注释 缩进啥的，前面的部分
        public string text;//单节点 text!=null && children =null ,父节点反之
        public List<StringNode> children;
        public string empty_back;//注释 缩进啥的，后面的部分
        public bool eof;
        public void Format(System.Text.StringBuilder sb)
        {
            if (this.text != null)//单节点
            {
                if (eof)//文件结束符号跳过
                    return;
                if (empty_fore != null)
                    sb.Append(empty_fore);
                sb.Append(text);
                if (empty_back != null)
                    sb.Append(empty_back);
            }
            else if (children != null)//父节点
            {
                foreach (var c in children)
                {
                    c.Format(sb);
                }
            }
        }
        public void FormatWithTreeInfo(System.Text.StringBuilder sb, int deep = 0, List<int> linestyle = null)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
            {

                space += " ";
                if (linestyle[i] > 0)
                {
                    if (i == deep - 1)
                    {
                        if (linestyle[i] == 1)
                            space += "|--";
                        else if (linestyle[i] == 2)
                        {
                            space += "\\--";
                            linestyle[i] = 0;
                        }
                    }
                    else
                        space += "|  ";
                }
                else
                {
                    space += "   ";
                }
            }
            var outline = space;

            if (type.Contains("Error"))
                outline += "*Err*";
            sb.Append("\n" + outline);

            if (this.text != null)//单节点
            {
                if (eof)//文件结束符号跳过
                    return;
                if (empty_fore != null)
                    sb.Append(empty_fore);

                sb.Append("<<" + type + ">>");

                sb.Append(text);
                if (empty_back != null)
                    sb.Append(empty_back);
            }
            else if (children != null)//父节点
            {
                sb.Append("<<" + type + ">>");
                if (deep == 0)
                {
                    linestyle = new List<int>();
                }
                linestyle.Add(children.Count > 1 ? 1 : 0);

                foreach (var c in children)
                {
                    c.FormatWithTreeInfo(sb, deep + 1, linestyle);
                }
            }

        }
        public static StringNode Parse(Antlr4.Runtime.Tree.IParseTree astnode, Dictionary<int, string> srclexs, string[] rulenames, string[] tokentypes, Dictionary<int, string> emptytext_fore, Dictionary<int, string> emptytext_back)
        {
            var node = new StringNode();

            //终结点
            var tnode = astnode as Antlr4.Runtime.Tree.ITerminalNode;
            if (tnode != null)
            {
                //终结点的type要去查
                node.type = tnode.Symbol.Type < 0 ? "<EOF>" : tokentypes[tnode.Symbol.Type];
                node.text = tnode.Symbol.Text;
                node.children = null;
                var ti = tnode.Symbol.TokenIndex;
                var beof = (tnode.Symbol.StopIndex - tnode.Symbol.StartIndex) < 0;
                if (beof)
                {
                    node.eof = true;
                }
                if (!emptytext_fore.TryGetValue(ti, out node.empty_fore))
                {
                    node.empty_fore = null;
                }
                if (!emptytext_back.TryGetValue(ti, out node.empty_back))
                {
                    node.empty_back = null;
                }
            }
            else
            {
                var rnode = astnode as Antlr4.Runtime.RuleContext;
                //语法节点
                node.type = rulenames[rnode.RuleIndex];
                node.text = null;
                node.empty_fore = null;
                node.empty_back = null;
                node.children = new List<StringNode>();
                for (var i = 0; i < astnode.ChildCount; i++)
                {
                    StringNode c = Parse(astnode.GetChild(i), srclexs, rulenames, tokentypes, emptytext_fore, emptytext_back);
                    node.children.Add(c);
                }
            }
            //this.srclexs = srclexs;
            //this.emptytext_fore = emptytext_fore;
            //this.emptytext_back = emptytext_back;

            //this.key = new StringItem(root.children[0] as Antlr4.Runtime.Tree.ITerminalNode);
            //this.id = new StringItem(root.StringLiteral());
            //this.leftBracket = new StringItem(root.children[2] as Antlr4.Runtime.Tree.ITerminalNode);



            return node;
            //3
            //4
            //this.rightBracket = new StringItem(root.children[root.children.Count - 2] as Antlr4.Runtime.Tree.ITerminalNode);


        }

        public StringNode FindOne_ByType(string type)
        {
            if (this.type == type)
                return this;
            if (children != null)
            {
                foreach (var c in children)
                {
                    if (c.type == type)
                        return c;
                }
                foreach (var c in children)
                {
                    var n = c.FindOne_ByType(type);
                    if (n != null)
                        return n;
                }
            }
            return null;
        }
        public IList<StringNode> FindAll_ByType(string type, IList<StringNode> nodes = null)
        {
            if (nodes == null) nodes = new List<StringNode>();
            if (children != null)
            {
                foreach (var c in children)
                {
                    if (c.type == type)
                        nodes.Add(c);
                }
                foreach (var c in children)
                {
                    c.FindAll_ByType(type, nodes);
                }
            }
            return nodes;
        }
    }

}
